package cn.mymanagesys.dao;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Properties;

public class BaseDao<T> {

    // 数据源对象
    private static DataSource dataSource = null;
    // dbutil 提供的一个执行SQL的对象
    private static QueryRunner queryRunner = null;

    // 定义一个变量来接收泛型的类型
    private Class<T> type;

    static {
        init();
    }

    // 获取T的Class对象，获取泛型的类型，泛型是在被子类继承时才确定
    public BaseDao() {

        // 获取子类的类型
        Class clazz = this.getClass();

        // 获取父类的类型，ParameterizedType表示的是带泛型的类型，
        // getGenericSuperclass()用来获取当前类的父类的类型
        ParameterizedType parameterizedType =
                (ParameterizedType) clazz.getGenericSuperclass();

        // 获取具体的泛型类型 getActualTypeArguments获取具体的泛型的类型，
        // 这个方法会返回一个Type的数组
        Type[] types =
                parameterizedType.getActualTypeArguments();

        // 获取具体的泛型的类型
        // noinspection unchecked
        this.type = (Class<T>) types[0];
    }

    /**
     * 读取配置文件
     */
    public static void init(){
        try {
            // 创建一个properties对象
            Properties properties = new Properties();
            //  声明一个变量，他的值是 配置文件名
            String configFile = "database.properties";
            // 将配置文件读取到输入流中
            InputStream inStream = BaseDao.class.getClassLoader().getResourceAsStream(configFile);
            // 从输入流中读取配置信息
            properties.load(inStream);

            dataSource = DruidDataSourceFactory
                    .createDataSource(properties);

            queryRunner = new QueryRunner(dataSource);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * druid获取连接
     * @return
     */
    public Connection getConnection(){
        Connection conn = null;
        // 建立连接，获取连接对象，要指定 连接字符串等
        try {
            conn = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

    /**
     * 释放资源
     * @param conn
     * @param stmt
     * @param rs
     */
    public void closeAll(Connection conn, Statement stmt, ResultSet rs){
        try {
            if (rs!=null){
                rs.close();
            }
            if (stmt!=null){
                stmt.close();
            }
            if (conn!=null){
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


    /**
     * 手动提交事务
     * 通用的-增删改-操作, 但是一般只用于修改
     * 这里使用的是用户自定义的 Connection 对象,这样的话用户可以自己控制事务
     * 其他的查询方法不提供 Connection 对象,因为查询不涉及事务的操作.
     */
    public int updateCommit(Connection conn, String sql, Object... params) {
        int count = 0;
        try {
            count = queryRunner.update(conn, sql, params);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return count;
    }

    /**
     * 数据的增删改
     * 默认一个SQL语句为一个事务,数据库事务交给DBUtils框架进行管理
     *
     * @param sql    SQL语句
     * @param params 执行参数
     * @return 返回受影响的行数
     */
    public int update(String sql, Object... params) {
        int count = 0;
        try {
            count = queryRunner.update(sql, params);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return count;
    }

    /**
     * 自动提交事务的查询方法
     *
     * @param sql    SQL 语句
     * @param params 查询参数
     * @return 返回泛型
     */
    public T queryBean(String sql, Object... params) {
        T t = null;
        try {
            t = queryRunner
                    .query(sql, new BeanHandler<>(type), params);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return t;
    }

    /**
     * 自动提交事务的查询所有方法
     *
     * @param sql    SQL 语句
     * @param params 查询参数
     * @return 返回泛型集合
     */
    public List<T> queryBeanList(String sql, Object... params) {
        List<T> list = null;
        try {
            list = queryRunner
                    .query(sql, new BeanListHandler<T>(type), params);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * 自动提交事务的值查询
     *
     * @param sql    SQL 语句
     * @param params 参数
     * @return 返回数值如 count(*) sum(total) ...
     */
    public Object queryValue(String sql, Object... params) {
        Object count = null;
        try {
            count = queryRunner
                    .query(sql, new ScalarHandler<>(), params);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return count;
    }

    /**
     * 处理事务提交与回滚
     *
     * @param connection 连接的对象
     */
    public void commit(Connection connection) {
        try {
            DbUtils.commitAndClose(connection);
        } catch (SQLException e) {
            System.out.println("事务提交失败!");
            DbUtils.rollbackAndCloseQuietly(connection);
            e.printStackTrace();
        }
    }
}
